package edu.pku.sei.mdabase.infrastructure.runtime;

import java.util.HashMap;
import java.util.Map;

import edu.pku.sei.mdabase.basictypes.OCLInvalid;
import edu.pku.sei.mdabase.basictypes.OCLVoidValue;
import edu.pku.sei.mdabase.infrastructure.model.MAttribute;
import edu.pku.sei.mdabase.infrastructure.model.MClass;
/**
 * use to record variable change history in context
 * record both committed or un-committed history
 * @author FengChao
 *
 */
public class ContextHistory {
	private int timestamp = 0;
	private Map<Object,VariableHistory> historyMap = new HashMap<Object,VariableHistory>();
	/**
	 * commit all latest change to variable
	 * all uncommitted history will be marked "commited"
	 * @return
	 */
	public boolean commitAll(){
		if(historyMap==null) 
			return true;
		
		for(Map.Entry<Object, VariableHistory> e : historyMap.entrySet()){
			Object key = e.getKey();
			VariableHistory history = e.getValue();
			history.commitAll();
		}
		
		return true;
	}
	/**
	 * restore all uncommitted history
	 * all uncommitted history will be deleted and can't undo
	 * @return
	 */
	public boolean restoreUncommit() {
		for(Map.Entry<Object, VariableHistory> e : historyMap.entrySet()){
			Object key = e.getKey();
			VariableHistory history = e.getValue();
			history.restoreUncommit();
		}
		return true;
	}
	
	/**
	 * restore variable value to latest value before timestamp
	 * if there is uncommitted value before timestamp, 
	 * the variable's value won't be changed, just discard all history after timestamp
	 * if all histories are committed before timestamp
	 * all history after timestamp will be deleted
	 * and variable's value will be restore the latest change before timestamp
	 * @param timestamp
	 * @return
	 */
	public boolean restoreToTime(int timestamp) {
		for(Map.Entry<Object, VariableHistory> e : historyMap.entrySet()){
			Object key = e.getKey();
			VariableHistory history = e.getValue();
			history.restoreToTime(timestamp);
		}
		return true;
	}
	
	/**
	 * get latest value of varaible 
	 * latest value may be both commited and uncommitted
	 * @param obj
	 * @return
	 */
	public Object getValue(Object obj) {
		assert obj!=null && obj!=OCLVoidValue.VOID && obj!=OCLVoidValue.NULL_VALUE && obj!=OCLInvalid.INVALID;
		
		VariableHistory history = historyMap.get(obj);
		if (history != null)
			return history.getCurrentValue();
		else if (obj instanceof Variable) {
			return ((Variable)obj).getValue();
		} else if (obj instanceof MAttribute) {
			return ((MAttribute)obj).getValue();
		} else {
			System.out.println("Unexpected Cache Entry: type="+obj);
			return null;
		}
	}
	/**
	 * set value of variable
	 * create a history to record this operation
	 * key represents the variable, may be instance of Variable or MAttribute 
	 * @param key
	 * @param newValue
	 * @return
	 */
	public boolean setValue(Object key, Object newValue) {
		VariableHistory history = historyMap.get(key);
		if (history == null) {
			history = new VariableHistory(key);
			history.parent = this;
			historyMap.put(key, history);
		}
		history.changeValue(newValue);
		return true;
	}
	
	public int getTimestamp() {
		timestamp++;
		return timestamp;
	}
	
	public boolean removeHistory(Variable v) {
		if (historyMap.get(v) != null) {
			historyMap.remove(v);
			return true;
		} else {
			return false;
		}
	}
	
	public boolean removeHistory(MAttribute a) {
		if (historyMap.get(a) != null) {
			historyMap.remove(a);
			return true;
		} else {
			return false;
		}
	}
	
	public boolean removeHistory(MClass c) {
		for (MAttribute attr : c.getAttributes()) {
			if (attr.getValue() instanceof MClass) {
				removeHistory((MClass)attr.getValue());
			}
			removeHistory(attr);
		}
		return true;
	}
	public boolean commit(Object variable) {
		VariableHistory vh = historyMap.get(variable);
		if (vh != null) {
			return vh.commitAll();
		}
		return false;
	}
	
}
